package com.zyq.intercepter;

import cn.hutool.core.util.StrUtil;
import com.zyq.annotation.PreAuthorize;
import com.zyq.constant.Constant;
import com.zyq.entity.SysPermission;
import com.zyq.entity.SysUser;
import com.zyq.enuns.ResponseCode;
import com.zyq.exception.BusinessException;
import com.zyq.service.IPermissionService;
import com.zyq.service.IUserService;
import com.zyq.util.JwtUtil;
import com.zyq.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * author xiaochi
 * date 2024/5/22 10:22
 */
@Slf4j
@Component("authorizeIntercepter")
public class AuthorizeIntercepter extends AbstractBaseInterceptor {
    @Autowired
    private IUserService userService;
    @Autowired
    private IPermissionService permissionService;

    @Override
    protected boolean execute(HttpServletRequest request, HttpServletResponse response, HandlerMethod method) {
        PreAuthorize preAuthorize = method.getMethodAnnotation(PreAuthorize.class);
        if (preAuthorize != null){
            if (!preAuthorize.login()){
                return true;
            }
            String token = request.getHeader(Constant.User.ACCESS_TOKEN);
            if (token == null || "".equals(token)){
                throw new BusinessException(ResponseCode.TOKEN_ERROR);
            }
            SysUser sysUser = JwtUtil.getPayload(token, SysUser.class);
            if (!JwtUtil.verify(token) || ObjectUtils.isEmpty(sysUser) || ObjectUtils.isEmpty(sysUser.getUsername())){
                throw new BusinessException(ResponseCode.TOKEN_NO_AVAIL);
            }
            String username = sysUser.getUsername();
            String userTokenKey = Constant.User.USER_TOKEN_KEY + username;
            String userKey = getEntryCacheKey(username);
            String permissionKey = getPermissionCacheKey(username);
            if (!(RedisUtil.hasKey(userKey) && RedisUtil.hasKey(permissionKey) && RedisUtil.hasKey(userTokenKey))){
                throw new BusinessException(ResponseCode.SYSTEM_USERNAME_OFFLINE);
            }
            if (!Objects.equals(token,RedisUtil.get(userTokenKey))){
                throw new BusinessException(ResponseCode.TOKEN_NO_AVAIL);
            }
//            String userKey = this.getEntryCacheKey(username);
//            String permissionKey = this.getPermissionCacheKey(username);

            // 1.先查询缓存，没有再查询数据库是否存在该用户
            SysUser user = RedisUtil.getByCache(userKey, Constant.User.USER_KEY_EXPIRE, SysUser.class, () -> userService.getUserByUserName(username));
            if (ObjectUtils.isEmpty(user)){
                throw new BusinessException(ResponseCode.SYSTEM_USERNAME_NOT_EXISTS);
            }
            String authorize = preAuthorize.authorize();
            if ("".equals(authorize)){
                return true;
            }
            // 2.先查询缓存，没有在查询数据库，查询用户所有权限
            List<SysPermission> permissionList =  RedisUtil.getByCache(permissionKey, Constant.User.USER_KEY_EXPIRE, SysPermission.class, () -> permissionService.permissionListByUserId(user.getId()));
            List<String> permsList = permissionList.stream().filter(item -> !StrUtil.isBlank(item.getPerms())).map(item -> item.getPerms()).collect(Collectors.toList());
            //if (permsList.stream().noneMatch(authorize::contains)){
            if (!isAuth(authorize,permsList)){
                throw new BusinessException(ResponseCode.NOT_PERMISSION);
            }
        }
        return true;
    }

    /**
     * 验证权限
     * @return
     */
    private boolean isAuth(String authorize,List<String> permsList){
        authorize = authorize.replaceAll(" ", "");
        List<String> auths = new ArrayList<>();
        if (StrUtil.contains(authorize,"OR") || StrUtil.contains(authorize,"||")){
            auths = StrUtil.split(authorize.replaceAll("OR","||"),"||");
        }
        boolean isAnd = StrUtil.contains(authorize,"AND") || StrUtil.contains(authorize,"&&");
        if (isAnd){
            auths = StrUtil.split(authorize.replaceAll("AND","&&"),"&&");
        }
        if (auths.isEmpty()){
            return permsList.contains(authorize);
        }
        boolean result = false;
        for (String auth : auths) {
            if (permsList.contains(auth)){
                result = true;
                if (!isAnd){
                    return true;
                }
            }else {
                if (isAnd){
                    return false;
                }
            }
        }
        return result;
    }

    public String getEntryCacheKey(String username){
        if (username == null){
            username = "";
        }
        return Constant.User.USER_KEY + username;
    }

    public String getPermissionCacheKey(String username){
        if (username == null){
            username = "";
        }
        return Constant.User.PERMISSION_KEY + username;
    }
}
